#include "edges.hpp"
#include "mesh.hpp"
#include "util.hpp"
#include "mesh_testFunctions.hpp"
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <utility>
#include <cmath>
#include <iostream>

using namespace hms;

using Catch::Approx;
// using namespace Catch;

TEST_CASE( "Edges::vertices" ){
	Mesh mesh;
	auto requireVerts = [&]( Index edge, Index vert1, Index vert2 ){
		REQUIRE(
			sameEntries(mesh.edge(edge).vertices(), Array2i{vert1, vert2})
		);
	};
	SECTION( "rectangular mesh" ){
		mesh = testMeshRectangles(true);
		requireVerts( 0, 1, 4);
		requireVerts( 1, 4, 7);
		requireVerts( 2, 3, 4);
		requireVerts( 3, 4, 5);
		requireVerts( 4, 6, 7);
		requireVerts( 5, 7, 8);
		requireVerts( 6, 0, 3);
		requireVerts( 7, 3, 6);
		requireVerts( 8, 0, 1);
		requireVerts( 9, 1, 2);
		requireVerts(10, 2, 5);
		requireVerts(11, 5, 8);
	}
	SECTION( "triangular mesh" ){
		mesh = testMeshTriangles(true);
		requireVerts( 0, 0, 2);
		requireVerts( 1, 0, 4);
		requireVerts( 2, 0, 3);
		requireVerts( 3, 0, 5);
		requireVerts( 4, 0, 1);
		requireVerts( 5, 1, 2);
		requireVerts( 6, 2, 3);
		requireVerts( 7, 3, 4);
		requireVerts( 8, 4, 5);
	}
	SECTION( "mixed mesh" ){
		mesh = testMeshMixed(true);
		requireVerts( 0, 1, 2);
		requireVerts( 1, 0, 2);
		requireVerts( 2, 0, 1);
		requireVerts( 3, 1, 3);
		requireVerts( 4, 2, 4);
		requireVerts( 5, 3, 4);
	}
}

TEST_CASE( "Calculating lengths of edges" )
{
	SECTION( "rectangular mesh" ){
		Mesh mesh { testMeshRectangles() };
		
		for (Index i {0}; i<mesh.nEdges(); ++i )
			REQUIRE( mesh.edges().length(i) == Approx(1) );
	}
	SECTION( "mixed mesh" ){
		Mesh mesh { testMeshMixed() };
		
		/* these are the lengths that should result from the test mesh */
		VectorXs lengths ( 6 );
		lengths <<
			std::sqrt(5), std::sqrt(5), 2, 2, 2, 2;
		
		REQUIRE( approxEqual( mesh.edges().lengths(), lengths ) );
	}
}


TEST_CASE( "Normal vectors" )
{
	SECTION( "mixed mesh" ){
		Mesh mesh { testMeshMixed(true) };
		REQUIRE( approxEqual( mesh.edge(0).normal(), Vector2s{ 0,-1}.normalized() ) );
		REQUIRE( approxEqual( mesh.edge(1).normal(), Vector2s{ 2, 1}.normalized() ) );
		REQUIRE( approxEqual( mesh.edge(2).normal(), Vector2s{-2, 1}.normalized() ) );
		REQUIRE( approxEqual( mesh.edge(3).normal(), Vector2s{-1, 0}.normalized() ) );
		REQUIRE( approxEqual( mesh.edge(4).normal(), Vector2s{ 1, 0}.normalized() ) );
		REQUIRE( approxEqual( mesh.edge(5).normal(), Vector2s{ 0,-1}.normalized() ) );
	}
}

TEST_CASE( "Boundary flags of edges" )
{
	SECTION( "rectangular mesh" )
	{
		Mesh mesh { testMeshRectangles(true) };
		
		for ( Index i {0}; i<mesh.nEdges(); ++i ){
			if ( i < mesh.nInnerEdges() )
				REQUIRE( mesh.edges().isBoundary(i) == false );
			if ( i >= mesh.nInnerEdges() )
				REQUIRE( mesh.edges().isBoundary(i) == true  );
		}
	}
}

TEST_CASE( "Adjacent cells" )
{
	SECTION( "rectangular mesh" ){
		Mesh mesh { testMeshRectangles(true) };
		
		REQUIRE( (mesh.edge( 0).cells() == Array2i{0, 1}).all() );
		REQUIRE( (mesh.edge( 1).cells() == Array2i{2, 3}).all() );
		REQUIRE( (mesh.edge( 2).cells() == Array2i{0, 2}).all() );
		REQUIRE( (mesh.edge( 3).cells() == Array2i{1, 3}).all() );
		REQUIRE( (mesh.edge( 4).cells() == Array2i{2, 4}).all() );
		REQUIRE( (mesh.edge( 5).cells() == Array2i{3, 5}).all() );
		REQUIRE( (mesh.edge( 6).cells() == Array2i{0, 6}).all() );
		REQUIRE( (mesh.edge( 7).cells() == Array2i{2, 7}).all() );
		REQUIRE( (mesh.edge( 8).cells() == Array2i{0, 8}).all() );
		REQUIRE( (mesh.edge( 9).cells() == Array2i{1, 9}).all() );
		REQUIRE( (mesh.edge(10).cells() == Array2i{1,10}).all() );
		REQUIRE( (mesh.edge(11).cells() == Array2i{3,11}).all() );
	}
	SECTION( "triangular mesh" ){
		Mesh mesh { testMeshTriangles(true) };
		
		REQUIRE( (mesh.edge( 0).cells() == Array2i{2, 3}).all() );
		REQUIRE( (mesh.edge( 1).cells() == Array2i{0, 1}).all() );
		REQUIRE( (mesh.edge( 2).cells() == Array2i{1, 2}).all() );
		REQUIRE( (mesh.edge( 3).cells() == Array2i{0, 4}).all() );
		REQUIRE( (mesh.edge( 4).cells() == Array2i{3, 5}).all() );
		REQUIRE( (mesh.edge( 5).cells() == Array2i{3, 6}).all() );
		REQUIRE( (mesh.edge( 6).cells() == Array2i{2, 7}).all() );
		REQUIRE( (mesh.edge( 7).cells() == Array2i{1, 8}).all() );
		REQUIRE( (mesh.edge( 8).cells() == Array2i{0, 9}).all() );
	}
	SECTION( "mixed mesh" ){
		Mesh mesh { testMeshMixed(true) };
		
		REQUIRE( (mesh.edge( 0).cells() == Array2i{0, 1}).all() );
		REQUIRE( (mesh.edge( 1).cells() == Array2i{0, 2}).all() );
		REQUIRE( (mesh.edge( 2).cells() == Array2i{0, 3}).all() );
		REQUIRE( (mesh.edge( 3).cells() == Array2i{1, 4}).all() );
		REQUIRE( (mesh.edge( 4).cells() == Array2i{1, 5}).all() );
		REQUIRE( (mesh.edge( 5).cells() == Array2i{1, 6}).all() );
	}
}